package com.john.utils.http;

import com.alibaba.fastjson.JSONObject;
import com.john.utils.common.ExceptionUtil;
import com.john.utils.common.UnicodeConverteUtil;
import com.john.utils.common.StringUtil;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class HttpSend {
    private static String charset = "UTF-8" ;
    private static boolean isHttpsrequest = false;
    private static boolean show_log = false;
    private static Object parameter = null;
    private static Header[] requestHeaders = HttpHeader.custom().build();
    private static Header[] responseHeader = null;
    private static Integer requestStatus = null;

    //是否重新发起请求
    private static boolean requestRetry = true;
    private static Integer requestRetryCount = 1;
    private static int socketTimeout = 2000;
    private static int connectTimeout = 2000;
    private static String httpRequest = "DELETE" ;
    private HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
    private CloseableHttpClient closeableHttpClient = this.httpClientBuilder.build();
    private HttpEntityEnclosing enclosing = new HttpEntityEnclosing();
    private HttpHost proxy = null;

    public HttpSend setProxy(String hostname, Integer port) {
        this.proxy = new HttpHost(hostname, port);
        return this;
    }

    public HttpSend sendPost(String url) {
        this.enclosing.setURI(urlReplaceAll(url));
        HttpSend.httpRequest = "POST" ;
        return this;
    }

    public HttpSend sendGet(String url) {
        this.enclosing.setURI(urlReplaceAll(url));
        HttpSend.httpRequest = "GET" ;
        return this;
    }

    public HttpSend sendPut(String url) {
        this.enclosing.setURI(urlReplaceAll(url));
        HttpSend.httpRequest = "PUT" ;
        return this;
    }

    public HttpSend sendDelete(String url) {
        this.enclosing.setURI(urlReplaceAll(url));
        HttpSend.httpRequest = "DELETE" ;
        return this;
    }

    public static HttpSend custom() {
        return new HttpSend();
    }

    public HttpSend setRequestHeaders(Header[] headers) {
        if (headers != null) {
            HttpSend.requestHeaders = headers;
        }
        return this;
    }

    public Header[] getRequestHeaders() {
        return HttpSend.requestHeaders;
    }

    public Header[] getResponseHeader() {
        return HttpSend.responseHeader;
    }

    public Integer getRequestStatus(){
        return HttpSend.requestStatus;
    }

    /**
     * 填充请求数据体
     *
     * @param parameter
     * @return
     */
    public HttpSend setParameter(Object parameter) {
        if ((parameter != null) && (!parameter.equals("" ))) {
            HttpSend.parameter = parameter;
        }
        return this;
    }

    public HttpSend setCharset(String charset) {
        if ((charset != null) && (!charset.equals("" ))) {
            HttpSend.charset = charset;
        }
        return this;
    }

    /**
     * 失败重试策略 3次
     *
     * @param requestRetry
     * @return
     */
    public HttpSend setRequestRetry(boolean requestRetry) {
        HttpSend.requestRetry = requestRetry;
        return this;
    }

    public HttpSend setHttpsRequest(boolean ISHTTPSREQUEST) {
        HttpSend.isHttpsrequest = ISHTTPSREQUEST;
        return this;
    }

    public HttpSend setHttpsShowLog(boolean SHOW_LOG) {
        HttpSend.show_log = SHOW_LOG;
        return this;
    }

    public HttpSend setResponseTimeout(int responseTimeout) {
        if (responseTimeout >= 1000) {
            HttpSend.socketTimeout = responseTimeout;
        }
        return this;
    }

    public HttpSend setConnectTimeout(int connectTimeout) {
        if (connectTimeout >= 1000) {
            HttpSend.connectTimeout = connectTimeout;
        }
        return this;
    }

    /**
     * 执行请求
     *
     * @return 结果
     */
    @SuppressWarnings("static-access" )
    public JSONObject execute() {
        try {
            HttpResponse response = this.enclosing.httpRequestControl();
            if (response != null) {
                return httpResponseControl(response);
            } else if (this.requestRetry) {
                while (this.requestRetryCount <= 3 && this.requestRetry) {    //请求失败,重试3次
                    showLog("[正在重新发起第[" + this.requestRetryCount + "]次请求...]" );
                    response = this.enclosing.httpRequestControl();
                    this.requestRetryCount++;
                    if (response != null) {
                        this.requestRetryCount = 1;
                        break;
                    }
                }
                if (response != null) {
                    return httpResponseControl(response);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void showLog(String content) {
        if (HttpSend.show_log) {
            System.out.println(content);
        }
    }

    private JSONObject httpResponseControl(HttpResponse response) {
        JSONObject jsonObject = new JSONObject();
        try {
            HttpEntity entity = response.getEntity();
            requestStatus = response.getStatusLine().getStatusCode();
            showLog("[status: " + response.getStatusLine().getStatusCode() + "]" );
            jsonObject.put("status",response.getStatusLine().getStatusCode());
            Header[] headers = new Header[requestHeaders.length + 1];
            for (int i = 0; i < requestHeaders.length; i++) {
                headers[i] = requestHeaders[i];
            }
            headers[(headers.length - 1)] = new BasicHeader("status" , String.valueOf(response.getStatusLine().getStatusCode()));
            requestHeaders = headers;
            showLog("************************************************************" );
            showLog("[ResponseHeader]" );
            Header[] header = response.getAllHeaders();
            responseHeader = header;
            for (int i = 0; i < header.length; i++) {
                showLog("[" + header[i] + "]" );
            }

            if ((entity != null)){
                String resultData = EntityUtils.toString(entity);
                //判断是否包含Unicode 字符
                if(UnicodeConverteUtil.isContainsUnicodeChar(resultData)){
                    resultData = UnicodeConverteUtil.unicode2String(resultData);
                }
                //判断是否json字符串
                if(StringUtil.isJson(resultData)){
                    jsonObject.put("resultData",JSONObject.parseObject(resultData));
                }else{
                    jsonObject.put("resultData",resultData);
                }
            }
            return jsonObject;
        } catch (Exception e) {
            jsonObject = null;
            e.printStackTrace();
        } finally {
            try {
                closeableHttpClient.close();
                HttpSend.requestHeaders = HttpHeader.custom().build(); // 关闭之后重新构造请求头
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return jsonObject;
    }

    private CloseableHttpClient TrustManagers() {
        try {
            showLog("加载https信任证书" );
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL" , "SunJSSE" );
            sslContext.init(null, tm, new SecureRandom());
            SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(sslContext);
            return HttpClients.custom().setSSLSocketFactory(ssf).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * URL参数拼接
     *
     * @param url
     * @param map
     * @return
     */
    public static String parameterMosaic(String url, Map<String, String> map) {
        String urls = "" ;
        if ((url != null) && (!url.equals("" ))) {
            urls = url;
        }
        if (map != null) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                if (urls.indexOf("?" ) != -1)
                    urls = urls + "&" + (String) entry.getKey() + "=" + (String) entry.getValue();
                else {
                    urls = urls + "?" + (String) entry.getKey() + "=" + (String) entry.getValue();
                }
            }
        }
        return urls;
    }

    /**
     * 解析Request请求流
     *
     * @param request
     * @return 结果
     */
    public static String getRequestInputStream(HttpServletRequest request) {
        try {
            InputStream inputStream = request.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8" );
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            StringBuffer buffer = new StringBuffer();
            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            return buffer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 网络文件下载
     *
     * @param savePath
     * @return
     */
    public String getDownload(String savePath) {
        try {
            HttpResponse response = this.enclosing.httpRequestControl();
            savePath = urlReplaceAll(savePath);
            if (response != null) {
                HttpEntity entity = response.getEntity();
                showLog("[status: " + response.getStatusLine().getStatusCode() + "]" );
                showLog("[ResponseHeader]" );
                Header[] header = response.getAllHeaders();
                for (int i = 0; i < header.length; i++) {
                    showLog("[" + header[i] + "]" );
                }
                InputStream inputStream = entity.getContent();
                String imageName = savePath.substring(savePath.lastIndexOf("/" ) + 1, savePath.length());
                String path = savePath.substring(0, savePath.lastIndexOf("/" )) + "8s8s" ;
                File file = new File(savePath);
                if (!file.exists()) {
                    file = new File(path);
                    String filePath = file.getAbsolutePath();
                    filePath = filePath.substring(0, filePath.length() - 4);
                    file.mkdirs();
                    savePath = (filePath + "/" + imageName).replace("\\" , "/" );
                    file.delete();
                    file = new File(savePath);
                    if (!file.exists()) {
                        file.createNewFile();
                        FileOutputStream fos = new FileOutputStream(file);
                        byte[] buf = new byte[8096];
                        int size = 0;
                        while ((size = inputStream.read(buf)) != -1) {
                            fos.write(buf, 0, size);
                        }
                        fos.close();
                        inputStream.close();
                    }
                }
            }
            showLog("[文件位置:" + savePath + "]" );
            return savePath;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                this.closeableHttpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static String urlReplaceAll(String url) {
        return url.replaceAll("\\\\" , "/" ).replaceAll("//" , "/" ).replaceAll("http:/" , "http://" ).replaceAll("https:/" , "https://" );
    }

    private class HttpEntityEnclosing extends HttpEntityEnclosingRequestBase {
        @SuppressWarnings("unchecked" )
        private HttpResponse httpRequestControl() {
            try {
                showLog("************************************************************" );
                showLog("[RequestHeader]" );
                showLog("[" + URLDecoder.decode(getRequestLine().toString(), HttpSend.charset) + "]" );
                setHeaders(HttpSend.requestHeaders);
                if (HttpSend.parameter != null && HttpSend.parameter instanceof Map) {
                    List<NameValuePair> nvps = new ArrayList<NameValuePair>();
                    Map<String, String> params = (Map<String, String>) HttpSend.parameter;
                    Set<String> keySet = params.keySet();
                    String log = "[\n   Parameter " ;
                    for (String key : keySet) {
                        nvps.add(new BasicNameValuePair(key, params.get(key)));
                        log += "\n\t" + key + ":" + params.get(key) + "\n" ;
                    }
                    log += "]" ;
                    showLog(log);
                    setEntity(new UrlEncodedFormEntity(nvps, HttpSend.charset));
                } else if (HttpSend.parameter != null && HttpSend.parameter instanceof String) {
                    showLog("[Parameter " + HttpSend.parameter + "]" );
                    setEntity(new StringEntity(HttpSend.parameter.toString(), HttpSend.charset));
                }
                RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(HttpSend.socketTimeout).setConnectTimeout(HttpSend.connectTimeout).build();
                if (proxy != null) {
                    requestConfig = RequestConfig.custom().setProxy(proxy).setSocketTimeout(HttpSend.socketTimeout).setConnectTimeout(HttpSend.connectTimeout).build();
                }
                setConfig(requestConfig);
                for (int i = 0; i < HttpSend.requestHeaders.length; i++) {
                    showLog("[" + HttpSend.requestHeaders[i] + "]" );
                }
                if (HttpSend.isHttpsrequest) {
                    HttpSend.this.closeableHttpClient = HttpSend.this.TrustManagers();
                }
                return HttpSend.this.closeableHttpClient.execute(this);
            } catch (HttpHostConnectException e) {
                showLog("[连接异常...确认地址是否正确,网络是否连接正常" + e.getMessage() + "]");
                showLog(ExceptionUtil.getExceptionMessage(e));
                requestRetry = false;
            } catch (ConnectTimeoutException e) {
                showLog("[请求超时...确认网络是否连接正常" + e.getMessage() + "]");
                showLog(ExceptionUtil.getExceptionMessage(e));
                requestRetry = false;
            } catch (SocketTimeoutException e) {
                showLog("[响应超时..." + e.getMessage() + "]");
                showLog(ExceptionUtil.getExceptionMessage(e));
                requestRetry = false;
            } catch (ParseException e) {
                showLog("[无法分析http响应..." + e.getMessage() + "]");
                showLog(ExceptionUtil.getExceptionMessage(e));
                requestRetry = false;
            } catch (UnknownHostException e) {
                showLog("[域名解析错误:" + e.getMessage() + "]" );
                showLog(ExceptionUtil.getExceptionMessage(e));
                requestRetry = false;
            }catch (Exception e){
                showLog("[未知异常..." + e.getMessage() + "]");
                showLog(ExceptionUtil.getExceptionMessage(e));
                requestRetry = false;
            }
            return null;
        }

        private void setURI(String url) {
            setURI(URI.create(HttpSend.urlReplaceAll(url)));
        }

        public String getMethod() {
            return HttpSend.httpRequest;
        }
    }
}